﻿#region Copyright (c) 2000-2013 Developer Express Inc.
/*
{*******************************************************************}
{                                                                   }
{       Developer Express .NET Component Library                    }
{                                                                   }
{                                                                   }
{       Copyright (c) 2000-2013 Developer Express Inc.              }
{       ALL RIGHTS RESERVED                                         }
{                                                                   }
{   The entire contents of this file is protected by U.S. and       }
{   International Copyright Laws. Unauthorized reproduction,        }
{   reverse-engineering, and distribution of all or any portion of  }
{   the code contained in this file is strictly prohibited and may  }
{   result in severe civil and criminal penalties and will be       }
{   prosecuted to the maximum extent possible under the law.        }
{                                                                   }
{   RESTRICTIONS                                                    }
{                                                                   }
{   THIS SOURCE CODE AND ALL RESULTING INTERMEDIATE FILES           }
{   ARE CONFIDENTIAL AND PROPRIETARY TRADE                          }
{   SECRETS OF DEVELOPER EXPRESS INC. THE REGISTERED DEVELOPER IS   }
{   LICENSED TO DISTRIBUTE THE PRODUCT AND ALL ACCOMPANYING .NET    }
{   CONTROLS AS PART OF AN EXECUTABLE PROGRAM ONLY.                 }
{                                                                   }
{   THE SOURCE CODE CONTAINED WITHIN THIS FILE AND ALL RELATED      }
{   FILES OR ANY PORTION OF ITS CONTENTS SHALL AT NO TIME BE        }
{   COPIED, TRANSFERRED, SOLD, DISTRIBUTED, OR OTHERWISE MADE       }
{   AVAILABLE TO OTHER INDIVIDUALS WITHOUT EXPRESS WRITTEN CONSENT  }
{   AND PERMISSION FROM DEVELOPER EXPRESS INC.                      }
{                                                                   }
{   CONSULT THE END USER LICENSE AGREEMENT FOR INFORMATION ON       }
{   ADDITIONAL RESTRICTIONS.                                        }
{                                                                   }
{*******************************************************************}
*/
#endregion Copyright (c) 2000-2013 Developer Express Inc.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using UColor.Core.Helpers;
#if !DEMO
using DevExpress.Xpf.LayoutControl;
using DevExpress.Xpf.Core;
#endif
namespace UColor.WPF.Helpers {
	public class DefferableValue<T> : BindingAndDisposable {
		bool loadingInProgress;
		bool valueLoaded;
		object value;
		Func<object> getValueCallback;
		public DefferableValue(Func<object> getValueCallback) {
			this.getValueCallback = getValueCallback;
		}
		public bool LoadingInProgress {
			get { return loadingInProgress; }
			private set { SetValue<bool>("LoadingInProgress", ref loadingInProgress, value, RaiseLoadingInProgressChanged); }
		}
		public bool ValueLoaded {
			get { return valueLoaded; }
			private set { SetValue<bool>("ValueLoaded", ref valueLoaded, value); }
		}
		public object Value {
			get { return this.value; }
			private set { SetValue("Value", ref this.value, value); }
		}
		public event ThePropertyChangedEventHandler<bool> LoadingInProgressChanged;
		public void Load() {
			if(ValueLoaded || LoadingInProgress) return;
			LoadingInProgress = true;
			object value = null;
			BackgroundHelper.DoInBackground(() => {
				value = this.getValueCallback();
			}, () => {
				Value = value;
				ValueLoaded = true;
				LoadingInProgress = false;
			});
		}
		void RaiseLoadingInProgressChanged(bool oldValue, bool newValue) {
			if(LoadingInProgressChanged != null)
				LoadingInProgressChanged(this, new ThePropertyChangedEventArgs<bool>(oldValue, newValue));
		}
		public override string ToString() {
			return Value == null ? "NULL" : Value.ToString();
		}
	}
	public enum TextTrimmingMode { Default, None, CharacterEllipsis, WordEllipsis }
	public static class TextTrimmingHelper {
		#region Dependency Properties
		public static readonly DependencyProperty TextTrimmingProperty;
		static TextTrimmingHelper() {
			Type ownerType = typeof(TextTrimmingHelper);
			TextTrimmingProperty = DependencyProperty.RegisterAttached("TextTrimming", typeof(TextTrimmingMode), ownerType, new PropertyMetadata(TextTrimmingMode.Default, RaiseTextTrimmingChanged));
		}
		#endregion
		public static void SetTextTrimming(TextBlock tb, TextTrimmingMode v) { tb.SetValue(TextTrimmingProperty, v); }
		public static TextTrimmingMode GetTextTrimming(TextBlock tb) { return (TextTrimmingMode)tb.GetValue(TextTrimmingProperty); }
		static void RaiseTextTrimmingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
			TextTrimmingMode newValue = (TextTrimmingMode)e.NewValue;
			if(newValue == TextTrimmingMode.Default) return;
			TextBlock tb = (TextBlock)d;
			switch(newValue) {
				case TextTrimmingMode.WordEllipsis: tb.TextTrimming = TextTrimming.WordEllipsis; break;
#if SL
				case TextTrimmingMode.CharacterEllipsis: tb.TextTrimming = TextTrimming.WordEllipsis; break;
#else
				case TextTrimmingMode.CharacterEllipsis: tb.TextTrimming = TextTrimming.CharacterEllipsis; break;
#endif
				default: tb.TextTrimming = TextTrimming.None; break;
			}
		}
	}
#if !DEMO
	public class TileLayoutControlExt : TileLayoutControl {
		#region Dependency Properties
		public static readonly DependencyProperty ComputedHorizontalScrollBarVisibilityProperty;
		public static readonly DependencyProperty ComputedLeftShadowVisibilityProperty;
		public static readonly DependencyProperty ComputedRightShadowVisibilityProperty;
		static TileLayoutControlExt() {
			Type ownerType = typeof(TileLayoutControlExt);
			ComputedHorizontalScrollBarVisibilityProperty = DependencyProperty.Register("ComputedHorizontalScrollBarVisibility", typeof(Visibility), ownerType, new PropertyMetadata(Visibility.Collapsed, RaiseComputedHorizontalScrollBarVisibilityChanged));
			ComputedLeftShadowVisibilityProperty = DependencyProperty.Register("ComputedLeftShadowVisibility", typeof(Visibility), ownerType, new PropertyMetadata(Visibility.Collapsed, RaiseComputedLeftShadowVisibilityChanged));
			ComputedRightShadowVisibilityProperty = DependencyProperty.Register("ComputedRightShadowVisibility", typeof(Visibility), ownerType, new PropertyMetadata(Visibility.Collapsed, RaiseComputedRightShadowVisibilityChanged));
		}
		static void RaiseComputedHorizontalScrollBarVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
			((TileLayoutControlExt)d).computedHorizontalScrollBarVisibilityValue = (Visibility)e.NewValue;
		}
		static void RaiseComputedLeftShadowVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
			((TileLayoutControlExt)d).computedLeftShadowVisibilityValue = (Visibility)e.NewValue;
		}
		static void RaiseComputedRightShadowVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
			((TileLayoutControlExt)d).computedRightShadowVisibilityValue = (Visibility)e.NewValue;
		}
		Visibility computedHorizontalScrollBarVisibilityValue = Visibility.Collapsed;
		Visibility computedLeftShadowVisibilityValue = Visibility.Collapsed;
		Visibility computedRightShadowVisibilityValue = Visibility.Collapsed;
		#endregion
		public TileLayoutControlExt() {
			LostMouseCapture += OnLostMouseCapture;
		}
		public Visibility ComputedHorizontalScrollBarVisibility { get { return computedHorizontalScrollBarVisibilityValue; } private set { SetValue(ComputedHorizontalScrollBarVisibilityProperty, value); } }
		public Visibility ComputedLeftShadowVisibility { get { return computedLeftShadowVisibilityValue; } private set { SetValue(ComputedLeftShadowVisibilityProperty, value); } }
		public Visibility ComputedRightShadowVisibility { get { return computedRightShadowVisibilityValue; } private set { SetValue(ComputedRightShadowVisibilityProperty, value); } }
		protected override Size ArrangeOverride(Size finalSize) {
			finalSize = base.ArrangeOverride(finalSize);
			ComputedHorizontalScrollBarVisibility = Controller.HorzScrollBar == null ? Visibility.Collapsed : Controller.IsHorzScrollBarVisible ? Visibility.Visible : Visibility.Collapsed;
			ComputedLeftShadowVisibility = ComputedHorizontalScrollBarVisibility == Visibility.Visible && Controller.HorzScrollParams.Position > 0.0 ? Visibility.Visible : Visibility.Collapsed;
			ComputedRightShadowVisibility = ComputedHorizontalScrollBarVisibility == Visibility.Visible && Controller.HorzScrollParams.Position < Controller.HorzScrollParams.MaxPosition ? Visibility.Visible : Visibility.Collapsed;
			return finalSize;
		}
		protected override LayoutProviderBase CreateLayoutProvider() {
			return new CustomTileLayoutProvider(this, new VerticalTileLayoutCalculator());
		}
		void OnLostMouseCapture(object sender, System.Windows.Input.MouseEventArgs e) {
			foreach(UIElement child in Children)
				IsMouseOverHelper.SetIsMouseOver(child, false);
		}
	}
	public class CustomTileLayoutInfo {
		public int Column { get; set; }
		public int Row { get; set; }
		public int RowSpan { get; set; }
		public int ColumnSpan { get; set; }
	}
	public abstract class CustomTileLayoutCalculator {
		public abstract void CalculateLayout(List<CustomTileLayoutInfo> items, int rowsCount, int columnsCount);
	}
	public class VerticalTileLayoutCalculator : CustomTileLayoutCalculator {
		class LayoutCell {
			public LayoutCell(int row, int column, int layoutIndex) {
				Row = row;
				Column = column;
				LayoutIndex = layoutIndex;
			}
			public int Row { get; private set; }
			public int Column { get; private set; }
			public int LayoutIndex { get; private set; }
			#region Equality
			public override int GetHashCode() {
				return LayoutIndex;
			}
			public static bool operator ==(LayoutCell cell1, LayoutCell cell2) {
				bool cell1IsNull = (object)cell1 == null;
				bool cell2IsNull = (object)cell2 == null;
				if(cell1IsNull && cell2IsNull) return true;
				if(cell1IsNull || cell2IsNull) return false;
				return cell1.LayoutIndex == cell2.LayoutIndex;
			}
			public static bool operator !=(LayoutCell cell1, LayoutCell cell2) {
				return !(cell1 == cell2);
			}
			public override bool Equals(object obj) {
				return this == obj as LayoutCell;
			}
			#endregion
		}
		class LayoutGrid {
			List<LayoutCell> occupiedCellsList = new List<LayoutCell>();
			Dictionary<LayoutCell, bool> occupiedCellsSet = new Dictionary<LayoutCell, bool>();
			public LayoutGrid(int cellsCount, int rowsCount) {
				RowsCount = rowsCount;
				ColumnsCount = (cellsCount + rowsCount - 1) / rowsCount;
			}
			public int ColumnsCount { get; private set; }
			public int RowsCount { get; private set; }
			public void AddColumn() {
				++ColumnsCount;
			}
			public LayoutCell GetCellByLayoutIndex(int layoutIndex) {
				return new LayoutCell(layoutIndex % RowsCount, layoutIndex / RowsCount, layoutIndex);
			}
			public LayoutCell GetCellByRowColumn(int row, int column) {
				return new LayoutCell(row, column, column * RowsCount + row);
			}
			public bool IsCellFree(LayoutCell cell) {
				return !occupiedCellsSet.ContainsKey(cell);
			}
			public bool IsAreaFree(LayoutCell cell, int rowSpan, int columnSpan, out bool finish) {
				int nextColumn = cell.Column + columnSpan;
				int nextRow = cell.Row + rowSpan;
				if(nextColumn > ColumnsCount) {
					finish = true;
					return false;
				}
				finish = false;
				if(nextRow > RowsCount) return false;
				for(int column = cell.Column; column < nextColumn; ++column) {
					for(int row = cell.Row; row < nextRow; ++row) {
						if(!IsCellFree(GetCellByRowColumn(row, column))) return false;
					}
				}
				return true;
			}
			public void OccupyArea(LayoutCell cell, int rowSpan, int columnSpan) {
				int nextColumn = cell.Column + columnSpan;
				int nextRow = cell.Row + rowSpan;
				for(int column = cell.Column; column < nextColumn; ++column) {
					for(int row = cell.Row; row < nextRow; ++row) {
						LayoutCell areaCell = GetCellByRowColumn(row, column);
						occupiedCellsList.Add(areaCell);
						occupiedCellsSet.Add(areaCell, true);
					}
				}
			}
		}
		public override void CalculateLayout(List<CustomTileLayoutInfo> items, int rowsCount, int columnsCount) {
			if(rowsCount < 0)
				rowsCount = 100;
			int cellsCount = 0;
			foreach(CustomTileLayoutInfo item in items) {
				cellsCount += item.ColumnSpan * item.RowSpan;
			}
			LayoutGrid grid = new LayoutGrid(cellsCount, rowsCount);
			int layoutIndex = 0;
			foreach(CustomTileLayoutInfo item in items) {
				if(item.ColumnSpan == 1 && item.RowSpan == 1) {
					++layoutIndex;
					continue;
				}
				LayoutCell itemCell = FindCell(rowsCount, ref cellsCount, grid, item.RowSpan, item.ColumnSpan, ref layoutIndex);
				grid.OccupyArea(itemCell, item.RowSpan, item.ColumnSpan);
				item.Row = itemCell.Row;
				item.Column = itemCell.Column;
			}
			layoutIndex = 0;
			foreach(CustomTileLayoutInfo item in items) {
				if(item.ColumnSpan != 1 || item.RowSpan != 1) {
					continue;
				}
				LayoutCell itemCell = FindCell(rowsCount, ref cellsCount, grid, item.RowSpan, item.ColumnSpan, ref layoutIndex);
				grid.OccupyArea(itemCell, item.RowSpan, item.ColumnSpan);
				item.Row = itemCell.Row;
				item.Column = itemCell.Column;
			}
		}
		LayoutCell FindCell(int rowsCount, ref int cellsCount, LayoutGrid grid, int rowSpan, int columnSpan, ref int startIndex) {
			int layoutIndex = startIndex;
			LayoutCell itemCell = FindCellForward(cellsCount, grid, rowSpan, columnSpan, ref layoutIndex);
			if(itemCell != null) {
				++startIndex;
				return itemCell;
			}
			layoutIndex = startIndex;
			itemCell = FindCellBackward(grid, rowSpan, columnSpan, ref layoutIndex);
			if(itemCell != null) {
				return itemCell;
			}
			while(true) {
				cellsCount += rowsCount;
				grid.AddColumn();
				itemCell = grid.GetCellByRowColumn(0, grid.ColumnsCount - columnSpan);
				bool finish;
				if(grid.IsAreaFree(itemCell, rowSpan, columnSpan, out finish)) {
					startIndex = itemCell.LayoutIndex + 1;
					return itemCell;
				}
			}
		}
		LayoutCell FindCellForward(int cellsCount, LayoutGrid grid, int rowSpan, int columnSpan, ref int itemIndex) {
			while(true) {
				LayoutCell cell = grid.GetCellByLayoutIndex(itemIndex);
				if(cell.LayoutIndex > cellsCount) break;
				bool finish;
				if(grid.IsAreaFree(cell, rowSpan, columnSpan, out finish)) return cell;
				if(finish) break;
				++itemIndex;
			}
			return null;
		}
		LayoutCell FindCellBackward(LayoutGrid grid, int rowSpan, int columnSpan, ref int itemIndex) {
			while(true) {
				--itemIndex;
				LayoutCell cell = grid.GetCellByLayoutIndex(itemIndex);
				if(cell.LayoutIndex < 0) break;
				bool finish;
				if(grid.IsAreaFree(cell, rowSpan, columnSpan, out finish)) return cell;
			}
			return null;
		}
	}
	public class OrdinarTileLayoutCalculator : CustomTileLayoutCalculator {
		public override void CalculateLayout(List<CustomTileLayoutInfo> items, int rowsCount, int columnsCount) {
			int column = 0;
			int row = -1;
			CustomTileLayoutInfo prevSmall = null;
			foreach(CustomTileLayoutInfo item in items) {
				if(prevSmall != null && item.ColumnSpan == 1) {
					item.Row = prevSmall.Row;
					item.Column = prevSmall.Column + 1;
					prevSmall = null;
					continue;
				}
				prevSmall = null;
				++row;
				if(rowsCount >= 0 && row >= rowsCount) {
					column += 2;
					row = 0;
				}
				item.Column = column;
				item.Row = row;
				if(item.ColumnSpan == 1)
					prevSmall = item;
			}
		}
	}
	public class CustomTileLayoutProvider : TileLayoutProvider {
		public CustomTileLayoutProvider(IFlowLayoutModel model, CustomTileLayoutCalculator layoutCalculator)
			: base(model) {
			customLayoutCalculator = layoutCalculator;
		}
		CustomTileLayoutCalculator customLayoutCalculator;
		public new FrameworkElements LayoutItems { get; private set; }
		protected override object GetGroupHeader(FlowLayoutLayerInfo groupFirstLayerInfo) {
			FrameworkElement firstItem = LayoutItems[groupFirstLayerInfo.FirstItemIndex];
			return TileLayoutControl.GetGroupHeader(firstItem);
		}
		protected override void CalculateLayout(FrameworkElements items, Rect bounds,
			CalculateMaximizedElementIndex calculateMaximizedElementIndex, MeasureItem measureItem, ArrangeItem arrangeItem) {
			var maximizedElementIndex = calculateMaximizedElementIndex(items);
			if(maximizedElementIndex != -1)
				PrepareMaximizedElement(items, maximizedElementIndex);
			double itemStartOffset = GetLayerContentStart(bounds);
			double layerStartOffset = GetLayerStart(bounds);
			var itemPosition = new FlowLayoutItemPosition(GetLayerStart(bounds), itemStartOffset);
			LayoutItems = items;
			LayerInfos.Clear();
			if(items.Count == 0) return;
			FlowLayoutItemSize item0Size = GetItemSize(measureItem(items[0]));
			FlowLayoutItemSize baseSize = item0Size;
			Tile tile = items[0] as Tile;
			bool large = tile != null && tile.Size == TileSize.Large;
			if(large)
				baseSize.Width = (baseSize.Width - Parameters.ItemSpace) / 2.0;
			List<List<CustomTileLayoutInfo>> customLayers = new List<List<CustomTileLayoutInfo>>();
			int rowsCount = (int)((GetLayerContentLength(bounds) + Parameters.ItemSpace) / (baseSize.Length + Parameters.ItemSpace));
			List<CustomTileLayoutInfo> customLayer = null;
			object groupTitle = null;
			for(int ii = 0; ii < items.Count; ++ii) {
				FrameworkElement iitem = items[ii];
				object newGroupTitle = TileLayoutControl.GetGroupHeader(iitem);
				bool beginNewCustomLayer = ii == 0 || !object.Equals(newGroupTitle, groupTitle) || FlowLayoutControl.GetIsFlowBreak(iitem);
				groupTitle = newGroupTitle;
				Tile tile1 = iitem as Tile;
				bool large1 = tile1 != null && tile1.Size == TileSize.Large;
				CustomTileLayoutInfo ctli = new CustomTileLayoutInfo() { RowSpan = 1, ColumnSpan = large1 ? 2 : 1 };
				if(beginNewCustomLayer) {
					if(customLayer != null)
						customLayers.Add(customLayer);
					customLayer = new List<CustomTileLayoutInfo>();
				}
				customLayer.Add(ctli);
			}
			if(customLayer != null)
				customLayers.Add(customLayer);
			int layerIndex = 0;
			int itemIndex = 0;
			int layerColumn = 0;
			foreach(List<CustomTileLayoutInfo> customLayer1 in customLayers) {
				int layerRowsCount = rowsCount;
				int layerColumnsCount = -1;
				customLayoutCalculator.CalculateLayout(customLayer1, layerRowsCount, layerColumnsCount);
				int maxRow = 0;
				int maxColumn = 0;
				foreach(CustomTileLayoutInfo item in customLayer1) {
					if(item.Column + item.ColumnSpan > maxColumn)
						maxColumn = item.Column + item.ColumnSpan;
					if(item.Row + item.RowSpan > maxRow)
						maxRow = item.Row + item.RowSpan;
				}
				layerRowsCount = maxRow;
				layerColumnsCount = maxColumn;
				FlowLayoutLayerInfo li = new FlowLayoutLayerInfo(itemIndex, true, new FlowLayoutItemPosition(layerStartOffset + layerColumn * baseSize.Width + (layerColumn - layerIndex) * Parameters.ItemSpace + layerIndex * Parameters.LayerSpace, itemStartOffset));
				li.Size = new FlowLayoutItemSize(layerColumnsCount == 0 ? 0 : layerColumnsCount * baseSize.Width + (layerColumnsCount - 1) * Parameters.ItemSpace, layerRowsCount == 0 ? 0 : layerRowsCount * baseSize.Length + (layerRowsCount - 1) * Parameters.ItemSpace);
				LayerInfos.Add(li);
				foreach(CustomTileLayoutInfo customTile in customLayer1) {
					FrameworkElement item = items[itemIndex];
					FlowLayoutItemSize itemSize = itemIndex == 0 ? item0Size : GetItemSize(measureItem(item));
					if(arrangeItem != null)
						arrangeItem(item, new FlowLayoutItemPosition(layerStartOffset + (layerColumn + customTile.Column) * baseSize.Width + (layerColumn + customTile.Column - layerIndex) * Parameters.ItemSpace + layerIndex * Parameters.LayerSpace, itemStartOffset + customTile.Row * (baseSize.Length + Parameters.ItemSpace)), itemSize);
					++itemIndex;
				}
				++layerIndex;
				layerColumn += layerColumnsCount;
			}
		}
	}
#endif
	public static class TextBoxHelper {
		#region Dependency Properties
		public static readonly DependencyProperty TextProperty;
		static TextBoxHelper() {
			Type ownerType = typeof(TextBoxHelper);
			TextProperty = DependencyProperty.RegisterAttached("Text", typeof(string), ownerType, new PropertyMetadata(Guid.NewGuid().ToString(), RaiseTextChanged));
		}
		#endregion
		public static string GetText(TextBox text) { return (string)text.GetValue(TextProperty); }
		public static void SetText(TextBox text, string v) { text.SetValue(TextProperty, v); }
		static void RaiseTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
			TextBox textBox = (TextBox)d;
			textBox.TextChanged -= OnTextBoxTextChanged;
			textBox.TextChanged += OnTextBoxTextChanged;
			textBox.Text = (string)e.NewValue;
		}
		static void OnTextBoxTextChanged(object sender, TextChangedEventArgs e) {
			TextBox textBox = (TextBox)sender;
			SetText(textBox, textBox.Text);
		}
	}
	public static class DataContextHelper {
		#region Dependency Properties
		public static readonly DependencyProperty DataContextOwnerProperty;
		static DataContextHelper() {
			Type ownerType = typeof(DataContextHelper);
			DataContextOwnerProperty = DependencyProperty.RegisterAttached("DataContextOwner", typeof(DependencyObjectExt), ownerType, new PropertyMetadata(null, RaiseDataContextOwnerChanged));
		}
		#endregion
		public static object GetDataContextOwner(FrameworkElement d) { return d.GetValue(DataContextOwnerProperty); }
		public static void SetDataContextOwner(FrameworkElement d, object v) { d.SetValue(DataContextOwnerProperty, v); }
		static void RaiseDataContextOwnerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
			DependencyObjectExt owner = (DependencyObjectExt)e.NewValue;
			if(owner == null) return;
			BindingOperations.SetBinding(owner, DependencyObjectExt.DataContextProperty, new Binding("DataContext") { Source = d, Mode = BindingMode.OneWay });
		}
	}
	public static class ExceptionHelper {
		public static string GetMessage(Exception e) {
			string message = string.Empty;
			for(Exception exception = e; exception != null; exception = exception.InnerException) {
				message += exception.Message + "\n";
			}
			return message;
		}
	}
	public class SimpleControl : Control {
		public SimpleControl() { }
	}
	public static class EnumHelper {
		public static T[] GetValues<T>() {
#if SL
			object[] values = (object[])DevExpress.Data.Mask.EnumHelper.GetValues(typeof(T));
			T[] ret = new T[values.Length];
			values.CopyTo(ret, 0);
			return ret;
#else
			return (T[])Enum.GetValues(typeof(T));
#endif
		}
	}
#if SL
	public static class FrameworkElementExtensions {
		public static object TryFindResource(this FrameworkElement element, object resourceKey) {
			object result = null;
			if(element.Resources.Contains(resourceKey)) {
				result = element.Resources[resourceKey];
			}
			FrameworkElement parent = (element.Parent == null ? VisualTreeHelper.GetParent(element) : element.Parent) as FrameworkElement;
			if(result == null && parent != null) {
				result = parent.TryFindResource(resourceKey);
			}
			return result;
		}
	}
#endif
	public static class ListExtensions {
		public static List<T> Convert<T>(this IList list) {
			return new List<T>(GetFilteredItems<T>(list, (t) => true));
		}
		public static T Find<T>(this IList<T> list, Predicate<T> predicate) where T : class {
			foreach(T item in list) {
				if(predicate(item)) return item;
			}
			return null;
		}
		public static List<T> Filter<T>(this IList list, Predicate<T> predicate) {
			return new List<T>(GetFilteredItems<T>(list, predicate));
		}
		public static List<T> Filter<T>(this IList<T> list, Predicate<T> predicate) {
			return new List<T>(GetFilteredItems<T>(list, predicate));
		}
		static IEnumerable<T> GetFilteredItems<T>(this IEnumerable list, Predicate<T> predicate) {
			foreach(T item in list) {
				if(predicate(item)) {
					yield return item;
				}
			}
		}
	}
	public class ForwardingHelper {
		public static readonly DependencyProperty ContentProperty;
		static ForwardingHelper() {
			Type ownerType = typeof(ForwardingHelper);
			ContentProperty = DependencyProperty.RegisterAttached("Content", typeof(object), ownerType, new PropertyMetadata(null));
		}
		public static void SetContent(DependencyObject element, object value) { element.SetValue(ContentProperty, value); }
		public static object GetContent(DependencyObject element) { return element.GetValue(ContentProperty); }
	}
}
